package com.wzapp.sitposture.eshop.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.egzosn.pay.common.bean.PayOrder;
import com.egzosn.pay.wx.api.WxPayConfigStorage;
import com.egzosn.pay.wx.api.WxPayService;
import com.egzosn.pay.wx.bean.WxTransactionType;
import com.icanpay.PaymentSetting;
import com.icanpay.enums.GatewayType;
import com.querydsl.core.QueryResults;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.wzapp.sitposture.common.constant.OrderStatusConst;
import com.wzapp.sitposture.common.constant.PayConst;
import com.wzapp.sitposture.common.error.CustomizedException;
import com.wzapp.sitposture.common.util.ToolUtil;
import com.wzapp.sitposture.config.redission.annotation.RedisLock;
import com.wzapp.sitposture.eshop.entity.Order;
import com.wzapp.sitposture.eshop.entity.OrderPay;
import com.wzapp.sitposture.eshop.entity.OrderStatus;
import com.wzapp.sitposture.eshop.entity.QOrder;
import com.wzapp.sitposture.eshop.pay.properties.AlipayProperties;
import com.wzapp.sitposture.eshop.pay.properties.WeChatPaymentProperties;
import com.wzapp.sitposture.eshop.repository.OrderPayRepository;
import com.wzapp.sitposture.eshop.repository.OrderRepository;
import com.wzapp.sitposture.eshop.repository.OrderStatusRepository;
import com.wzapp.sitposture.eshop.service.IdGeneratorService;
import com.wzapp.sitposture.eshop.service.OrderService;
import com.wzapp.sitposture.eshop.view.PayUrl;
import com.wzapp.sitposture.eshop.view.UserOrderDTO;
import com.wzapp.sitposture.setup.entity.Setup;
import com.wzapp.sitposture.setup.service.SetupService;
import com.wzapp.sitposture.usercenter.entity.QUser;
import com.wzapp.sitposture.usercenter.entity.User;
import com.wzapp.sitposture.usercenter.entity.UserIncome;
import com.wzapp.sitposture.usercenter.service.HuanXinService;
import com.wzapp.sitposture.usercenter.service.UserService;
import com.wzapp.sitposture.usercenter.view.RingAccountDTO;
import com.wzapp.sitposture.usercenter.view.UserDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.Sort;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by qianglong on 2018/1/26.
 */
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    OrderRepository orderRepository;

    @Autowired
    OrderStatusRepository orderStatusRepository;

    @Autowired
    IdGeneratorService idGeneratorService;

    @Autowired
    UserService userService;

    @Autowired
    Environment environment;

    @Autowired
    private WeChatPaymentProperties weChatPaymentProperties;

    @Autowired
    OrderPayRepository orderPayRepository;

    @Autowired
    SetupService setupService;

    private WxPayService wxPayService;

    private PaymentSetting paymentSetting;

    @Autowired
    private AlipayProperties alipayProperties;

    @Autowired
    HuanXinService huanXinService;

    @Autowired
    private JPAQueryFactory queryFactory;

    private final static int MAX_LEVEL = 3;

    @PostConstruct
    public void init() {
        WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage();
        wxPayConfigStorage.setMchId(weChatPaymentProperties.getMch_id());
        wxPayConfigStorage.setAppid(weChatPaymentProperties.getAppid());
        wxPayConfigStorage.setKeyPublic("");
        wxPayConfigStorage.setSecretKey(weChatPaymentProperties.getKey());
        wxPayConfigStorage.setNotifyUrl(weChatPaymentProperties.getNotifyurl());
        wxPayConfigStorage.setReturnUrl(weChatPaymentProperties.getReturnurl());
        wxPayConfigStorage.setSignType("MD5");
        wxPayConfigStorage.setInputCharset("utf-8");

        wxPayService = new WxPayService(wxPayConfigStorage);
    }

    @PostConstruct
    public void aliInit()  throws Exception {

        paymentSetting = new PaymentSetting(GatewayType.Alipay);
        paymentSetting.getMerchant().setAppId(alipayProperties.getAppid());
        paymentSetting.getMerchant().setEmail(alipayProperties.getSeller_email());
        paymentSetting.getMerchant().setPartner(alipayProperties.getPartner());
        paymentSetting.getMerchant().setKey(alipayProperties.getKey());
        paymentSetting.getMerchant().setPrivateKeyPem(alipayProperties.getPrivatekeypem());
        paymentSetting.getMerchant().setPublicKeyPem(alipayProperties.getPublicKeypem());
        paymentSetting.getMerchant().setNotifyUrl(new URI(alipayProperties.getNotifyurl()));
        paymentSetting.getMerchant().setReturnUrl(new URI(alipayProperties.getReturnurl()));
    }

    /**
     * 功能: 支付已有的订单 <br/>
     *
     * @param orderId
     * @param payMethod
     * @return
     * @author wangzhijun
     * @date 2018-04-26 11:44
     */
    @Override
    @RedisLock(key = "OrderServiceImpl.payOrder", value = "#orderId")
    public Map<String, Object> payOrder(String userId, String orderId, Integer payMethod, String terminalType) throws CustomizedException {

        if (ToolUtil.isEmpty(payMethod)) {
            throw new CustomizedException("eShop.order.pay.nonpaymethod");
        }

        Order orderDB = orderRepository.findOne(orderId);
        if(orderDB == null){
            throw new CustomizedException("eShop.order.not.exists", orderId);
        }
        //支付前验证订单当前状态
        if (OrderStatusConst.PAYED == orderDB.getStatus()) {
            throw new CustomizedException("eShop.order.pay.already", orderDB.getOrderCode());
        }

        String productName = "坐姿识别";
        String bodyName = "坐姿识别";

        orderDB.setPayMethod(payMethod);
        orderRepository.saveAndFlush(orderDB);

        Map<String, Object> result = new HashMap<>();
        result.put("id", orderDB.getId());
        result.put("userId", orderDB.getUserId());
        result.put("orderCode", orderDB.getOrderCode());
        result.put("payMethod", payMethod);
        result.put("amount",orderDB.getAmount());

        if (PayConst.PAY_METHOD_ALI.equals(payMethod)) {
            String url = null;
            try {
//                url = environment.getProperty("ql.domain") + "/eShop/orderPays/ali?orderCode=" + orderDB.getOrderCode()
//                        + "&amount=" + java.net.URLEncoder.encode(orderDB.getAmount() +"","utf-8");
                /*url = "http://everygod.com/api/pay/alipay/wappay/pay.jsp?WIDout_trade_no=" + saved.getCode()
                        + "&WIDsubject=" + java.net.URLEncoder.encode("testName")
                        + "&WIDtotal_amount=" + saved.getAmount() + "&WIDbody=" + java.net.URLEncoder.encode("商品购买");*/

                paymentSetting.getOrder().setOrderAmount(BigDecimal.valueOf(orderDB.getAmount()));
                paymentSetting.getOrder().setOrderNo(orderDB.getOrderCode());
                paymentSetting.getOrder().setSubject(productName);

                Map<String, String> bodyMap = paymentSetting.buildPayParams();
                url = bodyMap.get("body");

            } catch (Exception e) {
                throw new CustomizedException("eShop.order.pay.error");
            }

            PayUrl payUrl = new PayUrl();
            payUrl.setPayMethod(payMethod);
            payUrl.setUrl(url);
            payUrl.setReferer("");
            result.put("payUrl", payUrl);

        } else if (PayConst.PAY_METHOD_WEIXIN.equals(payMethod)) {
            // 微信App支付
            log.debug("===========appPay====weixin==");
            PayOrder payOrder = new PayOrder(productName, bodyName, new BigDecimal(orderDB.getAmount()), orderDB.getOrderCode());
            payOrder.setTransactionType(WxTransactionType.APP);
            Map<String, Object> data = wxPayService.orderInfo(payOrder);
            data.put("code", 0);

            log.debug("===========appPay===" + data);
            result.put("appPay", data);
        }

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Order updateOrderById(String id, String action) throws CustomizedException {

        Order orderDB = orderRepository.findOne(id);
        if(orderDB == null){
            throw new CustomizedException("eShop.order.not.exists",id);
        }

        Integer status = getOrderPostStatus(orderDB.getStatus(),action);
        orderDB.setStatus(status);
        orderRepository.saveAndFlush(orderDB);

        return  converterOrderView(orderDB);
    }

    @Override
    public Order getUserOrderByUserId(String userId) throws CustomizedException {

        Order orderDB = orderRepository.findTop1ByUserIdAndStatus(userId, OrderStatusConst.UNPAY);

        return orderDB;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Order createOrderInfo(Order order) throws CustomizedException {
        String orderCode = idGeneratorService.generatorOrderCode();
        order.setOrderCode(orderCode);
        order.setStatus(OrderStatusConst.UNPAY);
        order.setCreateTime(System.currentTimeMillis());
        order.setUpdateTime(System.currentTimeMillis());

        Order orderDB = orderRepository.save(order);

        this.saveOrderPay(orderDB.getUserId(), orderDB, PayConst.ORDER_PAY_UNKNOWN);

        return orderDB;
    }

    /**
     * 功能: 根据订单号支付订单<br/>
     *
     * @param id           订单id
     * @param payMethod    支付方式
     * @param payCode      第三方支付编码
     * @return com.eig.eshop.entity.Order
     * @author qianglong
     * @create 2018/5/5 10:41
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Order processPayOrderSuccess(String id, Integer payMethod, String payCode) throws
            CustomizedException {
        log.info("processPayOrderSuccess=======start");

        Order orderDB = orderRepository.findOne(id);
        if (orderDB == null) {
            throw new CustomizedException("eShop.order.not.exists", id);
        }

        //设置订单状态
        orderDB.setStatus(OrderStatusConst.PAYED);
        orderDB.setPayTime(System.currentTimeMillis());
        orderDB.setPayMethod(payMethod);
        orderDB.setPayCode(payCode);
        orderRepository.saveAndFlush(orderDB);

        UserDTO userDTO = userService.getSimpleUserById(orderDB.getUserId());
        if(userDTO != null) {
            String nickName = userDTO.getAccount();
            if(ToolUtil.isEmpty(nickName)){
                nickName = userDTO.getMobile();
            }
            //重新创建环信账号
            huanXinService.createUser(userDTO.getId(),userDTO.getPassword(),nickName);
        }

        //有推荐人，添加收益信息
        String recommendUserId = orderDB.getRecommendUserId();
        if(ToolUtil.isNotEmpty(recommendUserId)){
            Setup setup = setupService.findOneById("incomeLevel");
            JSONObject data = setup.getData();
            List<UserIncome> userIncomeList = new ArrayList<>(3);
            int incomeLevel = 1;

            if(userDTO != null){
                this.generateUserIncome(userIncomeList,recommendUserId,userDTO,data,incomeLevel);
                if(userIncomeList.size() > 0){
                    userService.saveUserIncome(userIncomeList);
                }
            }
        }

        log.info("processPayOrderSuccess=======end");
        return orderDB;
    }

    /**
     * @param userIncomeList
     * @param recommendUserId 推荐人id
     * @param buyerUser
     * @param data
     * @param incomeLevel
     */
    private void generateUserIncome(List<UserIncome> userIncomeList,String recommendUserId,UserDTO buyerUser,JSONObject data,int
            incomeLevel){
        UserDTO recommendUser = userService.getSimpleUserById(recommendUserId);
        if(recommendUser != null){
            Double amount = data.getDouble("level" + incomeLevel);
            String nickName = buyerUser.getAccount();
            if(ToolUtil.isEmpty(nickName)){
                nickName = buyerUser.getMobile();
            }
            //生成收益信息
            String info = "邀请了“" + nickName + "”获得收益" + amount + "元，预计5个工作日到账，节假日顺延！";
            UserIncome userIncome = new UserIncome();
            userIncome.setUserId(recommendUserId);
            userIncome.setAccount(recommendUser.getAccount());
            userIncome.setMobile(recommendUser.getMobile());
            userIncome.setAmount(amount);
            userIncome.setDeleted(false);
            userIncome.setInfo(info);
            userIncome.setCreateTime(System.currentTimeMillis());
            userIncome.setUpdateTime(System.currentTimeMillis());
            userIncomeList.add(userIncome);

            incomeLevel = incomeLevel + 1;
            String parentId = recommendUser.getParentId();
            //存在上级，向上找三级
            if(ToolUtil.isNotEmpty(parentId) && incomeLevel <= MAX_LEVEL){
                this.generateUserIncome(userIncomeList,parentId,buyerUser,data,incomeLevel);
            }
        }
    }

    @Async
    @Override
    public void autoCreateOrder() {
        //免费试用天数
        JSONObject jsonObject = setupService.findOneByGroupType("freeDays");
        int freeDays = jsonObject.getIntValue("freeDays");

        List<User> users = userService.findNewUserByCreateTime(freeDays);
        if(ToolUtil.isNotEmpty(users)){
            //产品费用
            JSONObject jsonObject2 = setupService.findOneByGroupType("productFee");
            Double productFee = jsonObject2.getDoubleValue("productFee");

            for (User user : users){
                Order order = new Order();
                order.setUserId(user.getId());
                order.setAmount(productFee);
                order.setPayAmount(productFee);
                order.setRecommendUserId(user.getParentId());
                try {
                    this.createOrderInfo(order);
                }catch (CustomizedException e){
                    log.error("===========创建订单失败=========userId={},userName={}======",user.getId(),user.getAccount());
                    log.error("==============错误原因===e={}==========",e.getMessage());
                }
            }
        }
    }

    @Async
    @Override
    public void autoDeleteUnpaidUserHuanXin() {

        List<String> userIds = userService.findUnpaidUserByCreateTime();

        if(ToolUtil.isNotEmpty(userIds)){
            log.info("=============autoDeleteUnpaidUserHuanXin====================size={}",userIds.size());
            for (String userId: userIds ) {
                try {
                    huanXinService.deleteRingAccountByUserId(userId);
                    Thread.sleep(500);
                }catch (Exception ex){
                    log.error("=================删除环信账号失败================userId={},error={}",userId,ex.getMessage());
                }
            }
        }

    }
    @Override
    public void createHuanXinByUserId(String userId){

        UserDTO userDTO = userService.getSimpleUserById(userId);
        if(userDTO != null) {
            String nickName = userDTO.getAccount();
            if(ToolUtil.isEmpty(nickName)){
                nickName = userDTO.getMobile();
            }
            //重新创建环信账号
            huanXinService.createUser(userDTO.getId(),userDTO.getPassword(),nickName);
        }
    }

    @Override
    public List<UserOrderDTO> findAllPaidUserOrder() {

        QUser qUser = QUser.user;
        QOrder qOrder = QOrder.order;

        BooleanExpression booleanExpression = qOrder.userId.eq(qUser.id).and(qOrder.status.eq(PayConst.ORDER_PAY_SUCCESS));
        booleanExpression = booleanExpression.and(qOrder.payMethod.in(PayConst.PAY_METHOD_ALI,PayConst.PAY_METHOD_WEIXIN));
        List<UserOrderDTO> queryResults = queryFactory.select(
                //构造返回对象
                Projections.constructor(UserOrderDTO.class, qUser.mobile, qUser.account,
                        qOrder.amount, qOrder.payMethod, qOrder.orderCode, qOrder.payCode, qOrder.payTime))
                .from(qOrder, qUser).where(booleanExpression)
                .orderBy(new OrderSpecifier(com.querydsl.core.types.Order.DESC, qOrder.payTime))
                .fetch();
        return queryResults;
    }

    private void saveOrderPay(String userId, Order orderDB, Integer status) {
        OrderPay orderPay = new OrderPay();
        orderPay.setPayMethod(orderDB.getPayMethod());
        orderPay.setStatus(status);
        orderPay.setOrderCode(orderDB.getOrderCode());
        orderPay.setOrderId(orderDB.getId());
        orderPay.setAmount(orderDB.getAmount());
        orderPay.setCreateTime(System.currentTimeMillis());
        orderPay.setUserId(userId);
        orderPayRepository.saveAndFlush(orderPay);
    }


    private Order converterOrderView(Order order){

        //脱离数据链接关系，不然以下修改，会被update
        Order newOrder = new Order();
        BeanUtils.copyProperties(order,newOrder);

        return newOrder;
    }

    //状态机
    private Integer getOrderPostStatus(Integer currentStatus,String operation) throws CustomizedException {

         OrderStatus orderStatus = orderStatusRepository.findByCurrentStatusAndOperation(currentStatus,operation);
         if(orderStatus == null){
            throw new CustomizedException("eShop.order.operation.notAllowed",operation);
         }
         return orderStatus.getPostStatus();
    }

}
